﻿using ClientBackBone.EDDN.Schema1;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace ClientBackBone.EDDN
{

    [DefaultPropertyAttribute("Name")]
    [Serializable]
    public class Commodity
    {
        public string Name { get; set; }
        public int BuyPrice { get; set; }
        public int Supply { get; set; }
        public int SellPrice { get; set; }
        public int Demand { get; set; }

        public override bool Equals(object obj)
        {            
            return obj.GetHashCode() == this.GetHashCode();
        }

        public override int GetHashCode()
        {
            return this.Name.GetHashCode();
        }        
    }

    [Serializable]
    public class Station
    {
        public int SinceLastVisit { get; set; }
        [DisplayName("System")]
        public string SystemName { get; set; }

        [DisplayName("Station")]
        public string StationName { get; set; }

        [DisplayName("Updated")]
        public DateTime updated { get; set; }

        public Dictionary<string,Commodity> Comodities;

        public override bool Equals(object obj)
        {
            Station that = obj as Station;
            if (that != null && string.Compare(this.StationName,that.StationName)==0 && string.Compare(this.SystemName,that.SystemName) ==0)
            {
                return true;
            }
            return false;
        }

        public override int GetHashCode()
        {
            return this.StationName.GetHashCode() + this.SystemName.GetHashCode();
        }
    }

    [Serializable]
    public class TradeData : INotifyPropertyChanged
    {

        public void AddStation(Station station)
        {
            Station oldStation = null;
            for(int i = 0; i < this.Stations.Count; i++)
            {
                if(this.Stations[i].Equals(station))
                {
                    oldStation= this.Stations[i];
                    this.Stations[i] = station;
                    break;
                }
            }

            if(oldStation == null)
            {
                this.Stations.Add(station);
                this.Stations.ForEach(f => f.SinceLastVisit++);
            } else if(oldStation.SinceLastVisit < 2)
            {
                this.Stations.ForEach(f => f.SinceLastVisit++);
            }

            this.RaisePropertyChanged("Stations");
        }

        public List<Recomendation> Recommendations 
        {
            get
            {
                return this.recommendations;
            }
            private set
            {
                this.recommendations = value;
                this.RaisePropertyChanged("Recommendations");
            }

        }

        public List<Station> Stations = new List<Station>();

        public Recomendation GetSingleRecomendation(string fromsystemName, string fromstationName, int cash, int capacity,string tosystemName, string toStationName)
        {
            Station home = this.Stations.Where(w => w.SystemName == fromsystemName && w.StationName == fromstationName).First();
            Station dest = this.Stations.Where(w => w.SystemName == tosystemName && w.StationName == toStationName).First();
            return this.GetSingleRecomendation(home, cash, capacity, dest);
        }

        private Recomendation GetSingleRecomendation(Station home, int cash, int capacity, Station dest)
        {
            var purcases = home.Comodities.Values.Where(w => w.Supply > 0)
                .Select(s => new Purcase{ Commodity = s, Quantitiy = Math.Min(Math.Min(cash / s.BuyPrice, capacity), s.Supply) });
            return this.GetSingleRecomendation(home, purcases, dest);
        }

        private Recomendation GetSingleRecomendation(Station home, IEnumerable<Purcase> purcases, Station dest)
        {
            var sysRecs = purcases.Select(sp =>
            {
                if (dest.Comodities.ContainsKey(sp.Commodity.Name))
                {
                    var stationComdity = dest.Comodities[sp.Commodity.Name];
                    if (stationComdity == null || stationComdity.SellPrice <= sp.Commodity.BuyPrice)
                    {
                        return null;
                    }
                    int quant = Math.Min(sp.Quantitiy, stationComdity.Demand);
                    int cost = quant * sp.Commodity.BuyPrice;
                    int value = quant * stationComdity.SellPrice;
                    return new Recomendation
                    {
                        Amount = quant,
                        Comodity = stationComdity.Name,
                        Cost = cost,
                        FromSystem = home.SystemName,
                        FromStation = home.StationName,
                        ForProfit = value - cost,
                        ToStation = dest.StationName,
                        ToSystem = dest.SystemName,
                        Value = value,
                        Recentcey = dest.updated
                    };
                }
                else
                {
                    return null;
                }
            });
            var stationBest = sysRecs.Where(wn => wn != null);
            if (stationBest.Count() > 0)
            {
                return stationBest.OrderByDescending(od => od.ForProfit).First();
            }
            else { return null; }
        }

        public void GetRecomendation(string systemName, string stationName, int cash, int capacity)
        {
            Station home = this.Stations.Where(w => w.SystemName == systemName && w.StationName == stationName).First();
            var purcases = home.Comodities.Values.Where(w => w.Supply > 0)
                .Select(s => new Purcase{ Commodity = s, Quantitiy = Math.Min(Math.Min(cash / s.BuyPrice, capacity), s.Supply) });

            var rec = this.Stations.Where(wx => !wx.Equals(home))
                .Select(s => this.GetSingleRecomendation(home,purcases,s)).Where(wn2 => wn2 !=null);
            if (rec.Count() > 0)
            {
                var x = rec.Select(o => 
                    o.ForProfit * (1f / Convert.ToSingle((DateTime.UtcNow - o.Recentcey).Hours + 1))).ToList();
                this.Recommendations = rec.OrderByDescending(o => o.ForProfit *(1f / (( DateTime.UtcNow - o.Recentcey).Duration().TotalHours + 1f) )).ToList();
            }
            else
            {
                this.Recommendations = new List<Recomendation>();
            }
        }

        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;

        [NonSerialized]
        private List<Recomendation> recommendations = new List<Recomendation>();

        private void RaisePropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }

        public void RemoveStation(string system, string station)
        {
            this.Stations.Remove(new Station { SystemName = system, StationName = station });
            this.RaisePropertyChanged("Stations");
        }
    }

    public class Recomendation
    {
        public string FromSystem { get; set; }
        public string FromStation { get; set; }
        public string Comodity { get; set; }
        public int Amount { get; set; }
        public int Cost { get; set; }
        public int Value { get; set; }
        public int ForProfit { get; set; }
        public string ToSystem { get; set; }
        public string ToStation { get; set; }
        public DateTime Recentcey { get; set; }
    }

    public class Purcase
    {
        public Commodity Commodity;
        public int Quantitiy;
    }
}
